package com.atguigu.search;

import java.util.ArrayList;
import java.util.List;

/**
 * @className: BinarySearch
 * @description: 二分查找。注意：数组必要有序
 * @date: 2021/3/13
 * @author: cakin
 */
public class BinarySearch {
    /**
     * 功能描述：二分查找测试
     *
     * @param args 命令行
     * @author cakin
     * @date 2021/3/13
     */
    public static void main(String[] args) {
        // 待查找的数组
        int arr[] = {1, 8, 10, 89, 1000, 1000, 1234};
        // int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
        // 二分法查找
        //int resIndex = binarySearch(arr, 0, arr.length - 1, 1000);
        //System.out.println("resIndex=" + resIndex);
        List<Integer> resIndexList = binarySearch2(arr, 0, arr.length - 1, 1000);
        System.out.println("resIndexList=" + resIndexList);
    }

    /**
     * 功能描述：二分查找算法
     *
     * @param arr     数组
     * @param left    左边的索引
     * @param right   右边的索引
     * @param findVal 要查找的值
     * @return int 如果找到就返回下标，如果没有找到，就返回 -1
     */
    public static int binarySearch(int[] arr, int left, int right, int findVal) {
        // 当 left > right 时，说明递归完整个数组，但是没有找到
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) { // 向右递归
            return binarySearch(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) { // 向左递归
            return binarySearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }
    }


    /**
     * 功能描述：二分法查找升级版，有多个相同的数值时，都能查找出来
     *
     * @param arr     数组
     * @param left    左边的索引
     * @param right   右边的索引
     * @param findVal 要查找的值
     * @return List<Integer> 找到索引下标列表
     * @author cakin
     * @date 2021/3/13
     * @description: 1 当找到 mid 索引值，不要马上返回。
     * 2 向 mid 索引值的左边扫描，将所有满足 1000 的元素的下标，加入到集合ArrayList
     * 3 向 mid 索引值的右边扫描，将所有满足 1000 的元素的下标，加入到集合ArrayList
     * 4 将Arraylist返回
     */
    public static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
        // System.out.println("调用次数");
        // 当 left > right 时，说明递归整个数组，但是没有找到
        if (left > right) {
            return new ArrayList<>();
        }
        int mid = (left + right) / 2;
        int midVal = arr[mid];
        if (findVal > midVal) { // 向右递归
            return binarySearch2(arr, mid + 1, right, findVal);
        } else if (findVal < midVal) { // 向左递归
            return binarySearch2(arr, left, mid - 1, findVal);
        } else {
            List<Integer> resIndexlist = new ArrayList<>();
            // 向 mid 索引值的左边扫描，将所有满足 1000 的元素的下标，加入到集合ArrayList
            int temp = mid - 1;
            while (true) {
                if (temp < 0 || arr[temp] != findVal) { // 退出
                    break;
                }
                // 将temp 放入到 resIndexlist
                resIndexlist.add(temp);
                temp -= 1; // temp左移
            }
            resIndexlist.add(mid);
            // 向 id 索引值的右边扫描，将所有满足 1000 的元素的下标，加入到集合ArrayList
            temp = mid + 1;
            while (true) {
                if (temp > arr.length - 1 || arr[temp] != findVal) { // 退出
                    break;
                }
                // temp 放入到 resIndexlist
                resIndexlist.add(temp);
                temp += 1; // temp右移
            }
            return resIndexlist;
        }
    }
}
